--- /dev/null
+/*
+ * gtkimmoduleime
+ * Copyright (C) 2003 Takuro Ashie
+ * Copyright (C) 2003-2004 Kazuki IWAMOTO
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * $Id$
+ */
+
+/*
+ * Please see the following site for the detail of Windows IME API.
+ * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/appendix/hh/appendix/imeimes2_35ph.asp
+ */
+
+#include "gtkimcontextime.h"
+
+#include "imm-extra.h"
+
+#include <gdk/gdkwin32.h>
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtkwidget.h>
+
+/* avoid warning */
+#ifdef STRICT
+# undef STRICT
+# include <pango/pangowin32.h>
+# ifndef STRICT
+# define STRICT 1
+# endif
+#else /* STRICT */
+# include <pango/pangowin32.h>
+#endif /* STRICT */
+
+/* #define BUFSIZE 4096 */
+
+#define FREE_PREEDIT_BUFFER(ctx) \
+{ \
+ g_free((ctx)->priv->comp_str); \
+ g_free((ctx)->priv->read_str); \
+ (ctx)->priv->comp_str = NULL; \
+ (ctx)->priv->read_str = NULL; \
+ (ctx)->priv->comp_str_len = 0; \
+ (ctx)->priv->read_str_len = 0; \
+}
+
+
+struct _GtkIMContextIMEPrivate
+{
+ /* save IME context when the client window is focused out */
+ DWORD conversion_mode;
+ DWORD sentence_mode;
+
+ LPVOID comp_str;
+ DWORD comp_str_len;
+ LPVOID read_str;
+ DWORD read_str_len;
+};
+
+
+/* GObject class methods */
+static void gtk_im_context_ime_class_init (GtkIMContextIMEClass *class);
+static void gtk_im_context_ime_init (GtkIMContextIME *context_ime);
+static void gtk_im_context_ime_dispose (GObject *obj);
+static void gtk_im_context_ime_finalize (GObject *obj);
+
+static void gtk_im_context_ime_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gtk_im_context_ime_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+/* GtkIMContext's virtual functions */
+static void gtk_im_context_ime_set_client_window (GtkIMContext *context,
+ GdkWindow *client_window);
+static gboolean gtk_im_context_ime_filter_keypress (GtkIMContext *context,
+ GdkEventKey *event);
+static void gtk_im_context_ime_reset (GtkIMContext *context);
+static void gtk_im_context_ime_get_preedit_string (GtkIMContext *context,
+ gchar **str,
+ PangoAttrList **attrs,
+ gint *cursor_pos);
+static void gtk_im_context_ime_focus_in (GtkIMContext *context);
+static void gtk_im_context_ime_focus_out (GtkIMContext *context);
+static void gtk_im_context_ime_set_cursor_location (GtkIMContext *context,
+ GdkRectangle *area);
+static void gtk_im_context_ime_set_use_preedit (GtkIMContext *context,
+ gboolean use_preedit);
+
+/* GtkIMContextIME's private functions */
+static void gtk_im_context_ime_set_preedit_font (GtkIMContext *context,
+ PangoFont *font);
+static GdkFilterReturn
+ gtk_im_context_ime_message_filter (GdkXEvent *xevent,
+ GdkEvent *event,
+ gpointer data);
+static void get_window_position (GdkWindow *win,
+ gint *x,
+ gint *y);
+static void cb_client_widget_hierarchy_changed (GtkWidget *widget,
+ GtkWidget *widget2,
+ GtkIMContextIME *context_ime);
+
+GType gtk_type_im_context_ime = 0;
+static GObjectClass *parent_class;
+
+
+void
+gtk_im_context_ime_register_type (GTypeModule *type_module)
+{
+ static const GTypeInfo im_context_ime_info = {
+ sizeof (GtkIMContextIMEClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gtk_im_context_ime_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (GtkIMContextIME),
+ 0,
+ (GInstanceInitFunc) gtk_im_context_ime_init,
+ };
+
+ gtk_type_im_context_ime =
+ g_type_module_register_type (type_module,
+ GTK_TYPE_IM_CONTEXT,
+ "GtkIMContextIME", &im_context_ime_info, 0);
+}
+
+static void
+gtk_im_context_ime_class_init (GtkIMContextIMEClass *class)
+{
+ GtkIMContextClass *im_context_class = GTK_IM_CONTEXT_CLASS (class);
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+
+ parent_class = g_type_class_peek_parent (class);
+
+ gobject_class->finalize = gtk_im_context_ime_finalize;
+ gobject_class->dispose = gtk_im_context_ime_dispose;
+ gobject_class->set_property = gtk_im_context_ime_set_property;
+ gobject_class->get_property = gtk_im_context_ime_get_property;
+
+ im_context_class->set_client_window = gtk_im_context_ime_set_client_window;
+ im_context_class->filter_keypress = gtk_im_context_ime_filter_keypress;
+ im_context_class->reset = gtk_im_context_ime_reset;
+ im_context_class->get_preedit_string = gtk_im_context_ime_get_preedit_string;
+ im_context_class->focus_in = gtk_im_context_ime_focus_in;
+ im_context_class->focus_out = gtk_im_context_ime_focus_out;
+ im_context_class->set_cursor_location = gtk_im_context_ime_set_cursor_location;
+ im_context_class->set_use_preedit = gtk_im_context_ime_set_use_preedit;
+}
+
+
+static void
+gtk_im_context_ime_init (GtkIMContextIME *context_ime)
+{
+ context_ime->client_window = NULL;
+ context_ime->toplevel = NULL;
+ context_ime->use_preedit = TRUE;
+ context_ime->preediting = FALSE;
+ context_ime->opened = FALSE;
+ context_ime->focus = FALSE;
+ context_ime->cursor_location.x = 0;
+ context_ime->cursor_location.y = 0;
+ context_ime->cursor_location.width = 0;
+ context_ime->cursor_location.height = 0;
+
+ context_ime->priv = g_malloc0 (sizeof (GtkIMContextIMEPrivate));
+ context_ime->priv->conversion_mode = 0;
+ context_ime->priv->sentence_mode = 0;
+ context_ime->priv->comp_str = NULL;
+ context_ime->priv->comp_str_len = 0;
+ context_ime->priv->read_str = NULL;
+ context_ime->priv->read_str_len = 0;
+}
+
+
+static void
+gtk_im_context_ime_dispose (GObject *obj)
+{
+ GtkIMContext *context = GTK_IM_CONTEXT (obj);
+ GtkIMContextIME *context_ime = GTK_IM_CONTEXT_IME (obj);
+
+ if (context_ime->client_window)
+ gtk_im_context_ime_set_client_window (context, NULL);
+
+ FREE_PREEDIT_BUFFER (context_ime);
+
+ if (G_OBJECT_CLASS (parent_class)->dispose)
+ G_OBJECT_CLASS (parent_class)->dispose (obj);
+}
+
+
+static void
+gtk_im_context_ime_finalize (GObject *obj)
+{
+ /* GtkIMContext *context = GTK_IM_CONTEXT (obj); */
+ GtkIMContextIME *context_ime = GTK_IM_CONTEXT_IME (obj);
+
+ g_free (context_ime->priv);
+ context_ime->priv = NULL;
+
+ if (G_OBJECT_CLASS (parent_class)->finalize)
+ G_OBJECT_CLASS (parent_class)->finalize (obj);
+}
+
+
+static void
+gtk_im_context_ime_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GtkIMContextIME *context_ime = GTK_IM_CONTEXT_IME (object);
+
+ g_return_if_fail (GTK_IS_IM_CONTEXT_IME (context_ime));
+
+ switch (prop_id)
+ {
+ default:
+ break;
+ }
+}
+
+
+static void
+gtk_im_context_ime_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkIMContextIME *context_ime = GTK_IM_CONTEXT_IME (object);
+
+ g_return_if_fail (GTK_IS_IM_CONTEXT_IME (context_ime));
+
+ switch (prop_id)
+ {
+ default:
+ break;
+ }
+}
+
+
+GtkIMContext *
+gtk_im_context_ime_new (void)
+{
+ return g_object_new (GTK_TYPE_IM_CONTEXT_IME, NULL);
+}
+
+
+static void
+gtk_im_context_ime_set_client_window (GtkIMContext *context,
+ GdkWindow *client_window)
+{
+ GtkIMContextIME *context_ime;
+ HWND hwnd = NULL;
+
+ g_return_if_fail (GTK_IS_IM_CONTEXT_IME (context));
+ context_ime = GTK_IM_CONTEXT_IME (context);
+
+ if (client_window)
+ {
+ HIMC himc;
+
+ hwnd = GDK_WINDOW_HWND (client_window);
+
+ /* get default ime context */
+ himc = ImmGetContext (hwnd);
+ context_ime->opened = ImmGetOpenStatus (himc);
+ ImmGetConversionStatus (himc,
+ &context_ime->priv->conversion_mode,
+ &context_ime->priv->sentence_mode);
+ ImmReleaseContext (hwnd, himc);
+
+ }
+ else if (context_ime->focus)
+ {
+ gtk_im_context_ime_focus_out (context);
+ }
+
+ context_ime->client_window = client_window;
+}
+
+
+static gboolean
+gtk_im_context_ime_filter_keypress (GtkIMContext *context,
+ GdkEventKey *event)
+{
+ GtkIMContextIME *context_ime;
+ HWND hwnd;
+ HIMC himc;
+ gboolean retval = FALSE;
+
+ g_return_val_if_fail (GTK_IS_IM_CONTEXT_IME (context), FALSE);
+ g_return_val_if_fail (event, FALSE);
+
+ if (event->type == GDK_KEY_RELEASE)
+ return retval;
+
+ context_ime = GTK_IM_CONTEXT_IME (context);
+ if (!context_ime->focus)
+ return FALSE;
+ if (!GDK_IS_WINDOW (context_ime->client_window))
+ return FALSE;
+
+ hwnd = GDK_WINDOW_HWND (context_ime->client_window);
+ himc = ImmGetContext (hwnd);
+
+ /* FIXME!! event->string is deprecated */
+ if (event->string && *event->string
+ && !g_unichar_iscntrl (g_utf8_get_char_validated (event->string,
+ strlen (event->string))))
+ {
+ g_signal_emit_by_name (G_OBJECT (context_ime), "commit", event->string);
+ retval = TRUE;
+ }
+
+ ImmReleaseContext (hwnd, himc);
+
+ return retval;
+}
+
+
+static void
+gtk_im_context_ime_reset (GtkIMContext *context)
+{
+ GtkIMContextIME *context_ime = GTK_IM_CONTEXT_IME (context);
+ HWND hwnd;
+ HIMC himc;
+
+ hwnd = GDK_WINDOW_HWND (context_ime->client_window);
+ himc = ImmGetContext (hwnd);
+ if (!himc)
+ return;
+
+ if (context_ime->preediting && ImmGetOpenStatus (himc))
+ ImmNotifyIME (himc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
+
+ context_ime->preediting = FALSE;
+ g_signal_emit_by_name (context, "preedit_changed");
+
+ ImmReleaseContext (hwnd, himc);
+}
+
+
+static gchar *
+get_utf8_preedit_string (GtkIMContextIME *context_ime, gint *pos_ret)
+{
+ gchar *utf8str = NULL;
+ HWND hwnd;
+ HIMC himc;
+ gint pos = 0;
+
+ if (pos_ret)
+ *pos_ret = 0;
+
+ /*
+ g_return_val_if_fail (GTK_IS_IM_CONTEXT_IME (context_ime),
+ g_strdup (""));
+ */
+
+ hwnd = GDK_WINDOW_HWND (context_ime->client_window);
+ himc = ImmGetContext (hwnd);
+
+ /* shouldn't return NULL */
+ g_return_val_if_fail (himc, g_strdup (""));
+
+ if (context_ime->preediting)
+ {
+ gpointer buf;
+ glong len;
+ GError *error = NULL;
+
+#ifdef UNICODE
+ len = ImmGetCompositionString (himc, GCS_COMPSTR, NULL, 0);
+ buf = g_malloc (len);
+ if (len > 0 && buf)
+ {
+ ImmGetCompositionString (himc, GCS_COMPSTR, buf, len);
+ len /= sizeof (gunichar2);
+ utf8str = g_utf16_to_utf8 (buf, len, NULL, NULL, &error);
+ if (error)
+ {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ }
+
+ if (pos_ret)
+ {
+ pos = ImmGetCompositionString (himc, GCS_CURSORPOS, NULL, 0);
+ if (pos < 0 || len < pos)
+ {
+ g_warning ("ImmGetCompositionString: "
+ "Invalid cursor position!");
+ pos = 0;
+ }
+ }
+ g_free (buf);
+ }
+#else /* not UNICODE */
+ len = ImmGetCompositionString (himc, GCS_COMPSTR, NULL, 0);
+ buf = g_malloc (len);
+ if (len > 0 && buf)
+ {
+ ImmGetCompositionString (himc, GCS_COMPSTR, buf, len);
+ utf8str = g_locale_to_utf8 (buf, len, NULL, NULL, &error);
+ if (error)
+ {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ }
+
+ if (pos_ret)
+ {
+ pos = ImmGetCompositionString (himc, GCS_CURSORPOS, NULL, 0);
+ /* get cursor position by offset */
+ if (pos < len && utf8str)
+ {
+ gchar *tmpstr;
+
+ tmpstr = g_locale_to_utf8 (buf, pos, NULL, NULL, &error);
+ if (error)
+ {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ }
+ if (tmpstr)
+ {
+ pos = g_utf8_strlen (tmpstr, -1);
+ }
+ else
+ {
+ pos = 0;
+ }
+ g_free (tmpstr);
+ }
+ else if (pos == len && utf8str)
+ {
+ pos = g_utf8_strlen (utf8str, -1);
+ }
+ else
+ {
+ g_warning ("ImmGetCompositionString: "
+ "Invalid cursor position!");
+ pos = 0;
+ }
+ }
+ g_free (buf);
+ }
+#endif /* not UNICODE */
+ }
+
+ if (!utf8str)
+ {
+ utf8str = g_strdup ("");
+ pos = 0;
+ }
+
+ if (pos_ret)
+ *pos_ret = pos;
+
+ ImmReleaseContext (hwnd, himc);
+
+ return utf8str;
+}
+
+
+static PangoAttrList *
+get_pango_attr_list (GtkIMContextIME *context_ime, const gchar *utf8str)
+{
+ PangoAttrList *attrs = pango_attr_list_new ();
+ HWND hwnd;
+ HIMC himc;
+
+ /* g_return_val_if_fail (GTK_IS_IM_CONTEXT_IME (context_ime), attr); */
+
+ hwnd = GDK_WINDOW_HWND (context_ime->client_window);
+ himc = ImmGetContext (hwnd);
+
+ g_return_val_if_fail (himc, attrs);
+
+ if (context_ime->preediting)
+ {
+ const gchar *schr = utf8str, *echr;
+ guint8 *buf;
+ guint16 f_red, f_green, f_blue, b_red, b_green, b_blue;
+ glong len, spos = 0, epos, sidx = 0, eidx;
+ PangoAttribute *attr;
+
+ /*
+ * get attributes list of IME.
+ */
+ len = ImmGetCompositionString (himc, GCS_COMPATTR, NULL, 0);
+ buf = g_malloc (len);
+ ImmGetCompositionString (himc, GCS_COMPATTR, buf, len);
+
+ /*
+ * schr and echr are pointer in utf8str.
+ */
+ for (echr = g_utf8_next_char (utf8str); *schr != '\0';
+ echr = g_utf8_next_char (echr))
+ {
+ /*
+ * spos and epos are buf(attributes list of IME) position by
+ * bytes.
+ * If UNICODE is defined, this value is same with UTF-8 offset.
+ * If it's not defined, this value is same with bytes position
+ * of locale encoded preedit string.
+ *
+ */
+#ifdef UNICODE
+ epos = g_utf8_pointer_to_offset (utf8str, echr);
+#else /* not UNICODE */
+ gchar *tmpstr;
+ GError *error = NULL;
+
+ epos = spos;
+ tmpstr = g_locale_from_utf8 (schr, echr - schr, NULL, NULL, &error);
+ if (error)
+ {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ }
+ if (tmpstr)
+ {
+ epos += strlen (tmpstr);
+ g_free (tmpstr);
+ }
+#endif /* not UNICODE */
+ /*
+ * sidx and eidx are positions in utf8str by bytes.
+ */
+ eidx = echr - utf8str;
+
+ /*
+ * convert attributes list to PangoAttriute.
+ */
+ if (*echr == '\0' || buf[spos] != buf[epos])
+ {
+ switch (buf[spos])
+ {
+ case ATTR_TARGET_CONVERTED:
+ attr = pango_attr_underline_new (PANGO_UNDERLINE_DOUBLE);
+ attr->start_index = sidx;
+ attr->end_index = eidx;
+ pango_attr_list_change (attrs, attr);
+ f_red = f_green = f_blue = 0;
+ b_red = b_green = b_blue = 0xffff;
+ break;
+ case ATTR_TARGET_NOTCONVERTED:
+ f_red = f_green = f_blue = 0xffff;
+ b_red = b_green = b_blue = 0;
+ break;
+ case ATTR_INPUT_ERROR:
+ f_red = f_green = f_blue = 0;
+ b_red = b_green = b_blue = 0x7fff;
+ break;
+ default: /* ATTR_INPUT,ATTR_CONVERTED,ATTR_FIXEDCONVERTED */
+ attr = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE);
+ attr->start_index = sidx;
+ attr->end_index = eidx;
+ pango_attr_list_change (attrs, attr);
+ f_red = f_green = f_blue = 0;
+ b_red = b_green = b_blue = 0xffff;
+ }
+ attr = pango_attr_foreground_new (f_red, f_green, f_blue);
+ attr->start_index = sidx;
+ attr->end_index = eidx;
+ pango_attr_list_change (attrs, attr);
+ attr = pango_attr_background_new (b_red, b_green, b_blue);
+ attr->start_index = sidx;
+ attr->end_index = eidx;
+ pango_attr_list_change (attrs, attr);
+
+ schr = echr;
+ spos = epos;
+ sidx = eidx;
+ }
+ }
+ g_free (buf);
+ }
+
+ ImmReleaseContext (hwnd, himc);
+
+ return attrs;
+}
+
+
+static void
+gtk_im_context_ime_get_preedit_string (GtkIMContext *context,
+ gchar **str,
+ PangoAttrList **attrs,
+ gint *cursor_pos)
+{
+ gchar *utf8str = NULL;
+ gint pos = 0;
+ GtkIMContextIME *context_ime;
+
+ context_ime = GTK_IM_CONTEXT_IME (context);
+
+ utf8str = get_utf8_preedit_string (context_ime, &pos);
+
+ if (attrs)
+ *attrs = get_pango_attr_list (context_ime, utf8str);
+
+ if (str)
+ {
+ *str = utf8str;
+ }
+ else
+ {
+ g_free (utf8str);
+ utf8str = NULL;
+ }
+
+ if (cursor_pos)
+ *cursor_pos = pos;
+}
+
+
+static void
+gtk_im_context_ime_focus_in (GtkIMContext *context)
+{
+ GtkIMContextIME *context_ime = GTK_IM_CONTEXT_IME (context);
+ GdkWindow *toplevel;
+ GtkWidget *widget = NULL;
+ HWND hwnd, top_hwnd;
+ HIMC himc;
+
+ if (!GDK_IS_WINDOW (context_ime->client_window))
+ return;
+
+ hwnd = GDK_WINDOW_HWND (context_ime->client_window);
+ himc = ImmGetContext (hwnd);
+ if (!himc)
+ return;
+
+ toplevel = gdk_window_get_toplevel (context_ime->client_window);
+ if (GDK_IS_WINDOW (toplevel))
+ {
+ gdk_window_add_filter (toplevel,
+ gtk_im_context_ime_message_filter, context_ime);
+ top_hwnd = GDK_WINDOW_HWND (toplevel);
+
+ context_ime->toplevel = toplevel;
+ }
+ else
+ {
+ g_warning ("gtk_im_context_ime_focus_in(): "
+ "cannot find toplevel window.");
+ return;
+ }
+
+ /* trace reparenting (probably no need) */
+ gdk_window_get_user_data (context_ime->client_window, (gpointer) & widget);
+ if (GTK_IS_WIDGET (widget))
+ {
+ g_signal_connect (G_OBJECT (widget), "hierarchy-changed",
+ G_CALLBACK (cb_client_widget_hierarchy_changed),
+ context_ime);
+ }
+ else
+ {
+ /* warning? */
+ }
+
+ /* swtich current context */
+ context_ime->focus = TRUE;
+
+ /* restore preedit context */
+ ImmSetConversionStatus (himc,
+ context_ime->priv->conversion_mode,
+ context_ime->priv->sentence_mode);
+
+ if (context_ime->opened)
+ {
+ if (!ImmGetOpenStatus (himc))
+ ImmSetOpenStatus (himc, TRUE);
+ if (context_ime->preediting)
+ {
+ ImmSetCompositionString (himc,
+ SCS_SETSTR,
+ context_ime->priv->comp_str,
+ context_ime->priv->comp_str_len, NULL, 0);
+ FREE_PREEDIT_BUFFER (context_ime);
+ }
+ }
+
+ /* clean */
+ ImmReleaseContext (hwnd, himc);
+}
+
+
+static void
+gtk_im_context_ime_focus_out (GtkIMContext *context)
+{
+ GtkIMContextIME *context_ime = GTK_IM_CONTEXT_IME (context);
+ GdkWindow *toplevel;
+ GtkWidget *widget = NULL;
+ HWND hwnd, top_hwnd;
+ HIMC himc;
+
+ if (!GDK_IS_WINDOW (context_ime->client_window))
+ return;
+
+ hwnd = GDK_WINDOW_HWND (context_ime->client_window);
+ himc = ImmGetContext (hwnd);
+ if (!himc)
+ return;
+
+ /* save preedit context */
+ ImmGetConversionStatus (himc,
+ &context_ime->priv->conversion_mode,
+ &context_ime->priv->sentence_mode);
+
+ if (ImmGetOpenStatus (himc))
+ {
+ gboolean preediting = context_ime->preediting;
+
+ if (preediting)
+ {
+ FREE_PREEDIT_BUFFER (context_ime);
+
+ context_ime->priv->comp_str_len
+ = ImmGetCompositionString (himc, GCS_COMPSTR, NULL, 0);
+ context_ime->priv->comp_str
+ = g_malloc (context_ime->priv->comp_str_len);
+ ImmGetCompositionString (himc, GCS_COMPSTR,
+ context_ime->priv->comp_str,
+ context_ime->priv->comp_str_len);
+
+ context_ime->priv->read_str_len
+ = ImmGetCompositionString (himc, GCS_COMPREADSTR, NULL, 0);
+ context_ime->priv->read_str
+ = g_malloc (context_ime->priv->read_str_len);
+ ImmGetCompositionString (himc, GCS_COMPREADSTR,
+ context_ime->priv->read_str,
+ context_ime->priv->read_str_len);
+ }
+
+ ImmSetOpenStatus (himc, FALSE);
+
+ context_ime->opened = TRUE;
+ context_ime->preediting = preediting;
+ }
+ else
+ {
+ context_ime->opened = FALSE;
+ context_ime->preediting = FALSE;
+ }
+
+ /* remove signal handler */
+ gdk_window_get_user_data (context_ime->client_window, (gpointer) & widget);
+ if (GTK_IS_WIDGET (widget))
+ {
+ g_signal_handlers_disconnect_by_func
+ (G_OBJECT (widget),
+ G_CALLBACK (cb_client_widget_hierarchy_changed), context_ime);
+ }
+
+ /* remove event fileter */
+ toplevel = gdk_window_get_toplevel (context_ime->client_window);
+ if (GDK_IS_WINDOW (toplevel))
+ {
+ gdk_window_remove_filter (toplevel,
+ gtk_im_context_ime_message_filter,
+ context_ime);
+ top_hwnd = GDK_WINDOW_HWND (toplevel);
+
+ context_ime->toplevel = NULL;
+ }
+ else
+ {
+ g_warning ("gtk_im_context_ime_focus_out(): "
+ "cannot find toplevel window.");
+ }
+
+ /* swtich current context */
+ context_ime->focus = FALSE;
+
+ /* clean */
+ ImmReleaseContext (hwnd, himc);
+}
+
+
+static void
+gtk_im_context_ime_set_cursor_location (GtkIMContext *context,
+ GdkRectangle *area)
+{
+ gint wx = 0, wy = 0;
+ GtkIMContextIME *context_ime;
+ COMPOSITIONFORM cf;
+ HWND hwnd;
+ HIMC himc;
+
+ g_return_if_fail (GTK_IS_IM_CONTEXT_IME (context));
+
+ context_ime = GTK_IM_CONTEXT_IME (context);
+ if (area)
+ context_ime->cursor_location = *area;
+
+ if (!context_ime->client_window)
+ return;
+
+ hwnd = GDK_WINDOW_HWND (context_ime->client_window);
+ himc = ImmGetContext (hwnd);
+ if (!himc)
+ return;
+
+ get_window_position (context_ime->client_window, &wx, &wy);
+ cf.dwStyle = CFS_POINT;
+ cf.ptCurrentPos.x = wx + context_ime->cursor_location.x;
+ cf.ptCurrentPos.y = wy + context_ime->cursor_location.y;
+ ImmSetCompositionWindow (himc, &cf);
+
+ ImmReleaseContext (hwnd, himc);
+}
+
+
+static void
+gtk_im_context_ime_set_use_preedit (GtkIMContext *context,
+ gboolean use_preedit)
+{
+ GtkIMContextIME *context_ime;
+
+ g_return_if_fail (GTK_IS_IM_CONTEXT_IME (context));
+ context_ime = GTK_IM_CONTEXT_IME (context);
+
+ context_ime->use_preedit = use_preedit;
+ if (context_ime->preediting)
+ {
+ /* FIXME */
+ HWND hwnd;
+ HIMC himc;
+
+ hwnd = GDK_WINDOW_HWND (context_ime->client_window);
+ himc = ImmGetContext (hwnd);
+ if (!himc)
+ return;
+ }
+}
+
+
+static void
+gtk_im_context_ime_set_preedit_font (GtkIMContext *context, PangoFont *font)
+{
+ GtkIMContextIME *context_ime;
+ GtkWidget *widget = NULL;
+ HWND hwnd;
+ HIMC himc;
+ PangoContext *pango_context;
+ LOGFONT *logfont;
+
+ g_return_if_fail (GTK_IS_IM_CONTEXT_IME (context));
+
+ context_ime = GTK_IM_CONTEXT_IME (context);
+ if (!context_ime->client_window)
+ return;
+
+ gdk_window_get_user_data (context_ime->client_window, (gpointer) &widget);
+ if (!GTK_IS_WIDGET (widget))
+ return;
+
+ hwnd = GDK_WINDOW_HWND (context_ime->client_window);
+ himc = ImmGetContext (hwnd);
+ if (!himc)
+ return;
+
+ /* set font */
+ pango_context = gtk_widget_get_pango_context (widget);
+ if (!pango_context)
+ goto ERROR_OUT;
+
+ if (!font)
+ font = pango_context_load_font (pango_context, widget->style->font_desc);
+ if (!font)
+ goto ERROR_OUT;
+
+ logfont = pango_win32_font_logfont (font);
+ if (logfont)
+ ImmSetCompositionFont (himc, logfont);
+
+ERROR_OUT:
+ /* clean */
+ ImmReleaseContext (hwnd, himc);
+}
+
+
+static GdkFilterReturn
+gtk_im_context_ime_message_filter (GdkXEvent *xevent,
+ GdkEvent *event,
+ gpointer data)
+{
+ GtkIMContext *context;
+ GtkIMContextIME *context_ime;
+ HWND hwnd;
+ HIMC himc;
+ MSG *msg = (MSG *) xevent;
+ GdkFilterReturn retval = GDK_FILTER_CONTINUE;
+
+ g_return_val_if_fail (GTK_IS_IM_CONTEXT_IME (data), retval);
+
+ context = GTK_IM_CONTEXT (data);
+ context_ime = GTK_IM_CONTEXT_IME (data);
+ if (!context_ime->focus)
+ return retval;
+
+ hwnd = GDK_WINDOW_HWND (context_ime->client_window);
+ himc = ImmGetContext (hwnd);
+ if (!himc)
+ return retval;
+
+ switch (msg->message)
+ {
+ case WM_IME_COMPOSITION:
+ {
+ gint wx = 0, wy = 0;
+ CANDIDATEFORM cf;
+
+ get_window_position (context_ime->client_window, &wx, &wy);
+ /* FIXME! */
+ {
+ HWND hwnd_top;
+ POINT pt;
+ RECT rc;
+
+ hwnd_top =
+ GDK_WINDOW_HWND (gdk_window_get_toplevel
+ (context_ime->client_window));
+ GetWindowRect (hwnd_top, &rc);
+ pt.x = wx;
+ pt.y = wy;
+ ClientToScreen (hwnd_top, &pt);
+ wx = pt.x - rc.left;
+ wy = pt.y - rc.top;
+ }
+ cf.dwIndex = 0;
+ cf.dwStyle = CFS_CANDIDATEPOS;
+ cf.ptCurrentPos.x = wx + context_ime->cursor_location.x;
+ cf.ptCurrentPos.y = wy + context_ime->cursor_location.y
+ + context_ime->cursor_location.height;
+ ImmSetCandidateWindow (himc, &cf);
+
+ if ((msg->lParam & GCS_COMPSTR))
+ g_signal_emit_by_name (context, "preedit_changed");
+
+ if (msg->lParam & GCS_RESULTSTR)
+ {
+ gsize len;
+ gpointer buf;
+ gchar *utf8str = NULL;
+ GError *error = NULL;
+
+ len = ImmGetCompositionString (himc, GCS_RESULTSTR, NULL, 0);
+ buf = g_alloca (len);
+ if (len > 0 && buf)
+ {
+ ImmGetCompositionString (himc, GCS_RESULTSTR, buf, len);
+#ifdef UNICODE
+ len /= sizeof (gunichar2);
+ utf8str = g_utf16_to_utf8 (buf, len, NULL, NULL, &error);
+#else /* not UNICODE */
+ utf8str = g_locale_to_utf8 (buf, len, NULL, NULL, &error);
+#endif /* not UNICODE */
+ if (error)
+ {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ }
+ }
+
+ if (utf8str)
+ {
+ g_signal_emit_by_name (G_OBJECT (context), "commit", utf8str);
+ g_free (utf8str);
+ }
+ }
+
+ if (context_ime->use_preedit)
+ retval = TRUE;
+ break;
+ }
+
+ case WM_IME_STARTCOMPOSITION:
+ context_ime->preediting = TRUE;
+ gtk_im_context_ime_set_cursor_location (context, NULL);
+ g_signal_emit_by_name (context, "preedit_start");
+ if (context_ime->use_preedit)
+ retval = TRUE;
+ break;
+
+ case WM_IME_ENDCOMPOSITION:
+ context_ime->preediting = FALSE;
+ g_signal_emit_by_name (context, "preedit_changed");
+ g_signal_emit_by_name (context, "preedit_end");
+ if (context_ime->use_preedit)
+ retval = TRUE;
+ break;
+
+ case WM_IME_NOTIFY:
+ switch (msg->wParam)
+ {
+ case IMN_SETOPENSTATUS:
+ context_ime->opened = ImmGetOpenStatus (himc);
+ gtk_im_context_ime_set_preedit_font (context, NULL);
+ break;
+
+ default:
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ ImmReleaseContext (hwnd, himc);
+ return retval;
+}
+
+
+/*
+ * x and y must be initialized to 0.
+ */
+static void
+get_window_position (GdkWindow *win, gint *x, gint *y)
+{
+ GdkWindow *parent, *toplevel;
+ gint wx, wy;
+
+ g_return_if_fail (GDK_IS_WINDOW (win));
+ g_return_if_fail (x && y);
+
+ gdk_window_get_position (win, &wx, &wy);
+ *x += wx;
+ *y += wy;
+ parent = gdk_window_get_parent (win);
+ toplevel = gdk_window_get_toplevel (win);
+
+ if (parent && parent != toplevel)
+ get_window_position (parent, x, y);
+}
+
+
+/*
+ * probably, this handler isn't needed.
+ */
+static void
+cb_client_widget_hierarchy_changed (GtkWidget *widget,
+ GtkWidget *widget2,
+ GtkIMContextIME *context_ime)
+{
+ GdkWindow *new_toplevel;
+
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+ g_return_if_fail (GTK_IS_IM_CONTEXT_IME (context_ime));
+
+ if (!context_ime->client_window)
+ return;
+ if (!context_ime->focus)
+ return;
+
+ new_toplevel = gdk_window_get_toplevel (context_ime->client_window);
+ if (context_ime->toplevel == new_toplevel)
+ return;
+
+ /* remove filter from old toplevel */
+ if (GDK_IS_WINDOW (context_ime->toplevel))
+ {
+ gdk_window_remove_filter (context_ime->toplevel,
+ gtk_im_context_ime_message_filter,
+ context_ime);
+ }
+ else
+ {
+ }
+
+ /* add filter to new toplevel */
+ if (GDK_IS_WINDOW (new_toplevel))
+ {
+ gdk_window_add_filter (new_toplevel,
+ gtk_im_context_ime_message_filter, context_ime);
+ }
+ else
+ {
+ }
+
+ context_ime->toplevel = new_toplevel;
+}